import { computed, defineComponent, onMounted, onUnmounted, ref, watchEffect } from 'vue';
import { getPixelRatio, getStyleStr, reRendering, rotateWatermark } from './utils';

export interface WatermarkFontType {
    color?: string
    fontSize?: number | string
    fontWeight?: 'normal' | 'light' | 'weight' | number
    fontStyle?: 'none' | 'normal' | 'italic' | 'oblique'
    fontFamily?: string
}

export interface WatermarkProps {
    zIndex?: number
    rotate?: number
    width?: number
    height?: number
    image?: string
    content: string | string[]
    font?: WatermarkFontType
    gap?: number[]
    offset?: number[]
}

const BaseSize = 2;
const FontGap = 3;

export default defineComponent({
    name: 'Watermark',
    props: [
        'zIndex',
        'rotate',
        'width',
        'height',
        'image',
        'content',
        'font',
        'gap',
        'offset'
    ],
    setup (props, { slots }) {
        const containerRef = ref<HTMLElement | null>(null);
        const watermarkRef = ref<HTMLElement | null>(null);
        const stopObservation = ref(false);
        const observer = ref<MutationObserver | null>(null);

        // 计算属性
        const gap = computed(() => props.gap ?? [100, 100]);
        const gapX = computed(() => gap.value[0] ?? 100);
        const gapY = computed(() => gap.value[1] ?? 100);
        const gapXCenter = computed(() => gapX.value / 2);
        const gapYCenter = computed(() => gapY.value / 2);
        const offsetLeft = computed(() => props.offset?.[0] ?? gapXCenter.value);
        const offsetTop = computed(() => props.offset?.[1] ?? gapYCenter.value);
        const fontSize = computed(() => props.font?.fontSize ?? 14);
        const fontWeight = computed(() => props.font?.fontWeight ?? 'normal');
        const fontStyle = computed(() => props.font?.fontStyle ?? 'normal');
        const fontFamily = computed(() => props.font?.fontFamily ?? 'sans-serif');
        const color = computed(() => props.font?.color ?? 'rgba(0,0,0,.26)');

        // 样式计算
        const markStyle = computed(() => {
            const style: Record<string, any> = {
                'z-index': props.zIndex ?? 9,
                position: 'absolute',
                left: '0px',
                top: '0px',
                width: '100%',
                height: '100%',
                'pointer-events': 'none',
                'background-repeat': 'repeat',
                'background-position': ''
            };

            let positionLeft = offsetLeft.value - gapXCenter.value;
            let positionTop = offsetTop.value - gapYCenter.value;

            if (positionLeft > 0) {
                style.left = `${positionLeft}px`;
                style.width = `calc(100% - ${positionLeft}px)`;
                positionLeft = 0;
            }
            if (positionTop > 0) {
                style.top = `${positionTop}px`;
                style.height = `calc(100% - ${positionTop}px)`;
                positionTop = 0;
            }

            style['background-position'] = `${positionLeft}px ${positionTop}px`;
            return style;
        });


        // 方法
        const destroyWatermark = () => {
            if (watermarkRef.value) {
                watermarkRef.value.remove();
                watermarkRef.value = null;
            }
        };

        const appendWatermark = (base64Url: string, markWidth: number) => {
            if (containerRef.value && watermarkRef.value) {
                stopObservation.value = true;
                watermarkRef.value.setAttribute(
                    'style',
                    getStyleStr({
                        ...markStyle.value,
                        'background-image': `url('${base64Url}')`,
                        'background-size': `${(gapX.value + markWidth) * BaseSize}px`
                    })
                );
                containerRef.value.append(watermarkRef.value);
                setTimeout(() => {
                    stopObservation.value = false;
                });
            }
        };


        const getMarkSize = (ctx: CanvasRenderingContext2D) => {
            let defaultWidth = 120;
            let defaultHeight = 64;
            const content = props.content;
            const image = props.image;
            const width = props.width;
            const height = props.height;

            if (!image && ctx.measureText) {
                ctx.font = `${Number(fontSize.value)}px ${fontFamily.value}`;
                const contents = Array.isArray(content) ? content : [content];
                const widths = contents.map((item: string) => ctx.measureText(item).width);
                defaultWidth = Math.ceil(Math.max(...widths));
                defaultHeight = Number(fontSize.value) * contents.length + (contents.length - 1) * FontGap;
            }

            return [width ?? defaultWidth, height ?? defaultHeight] as const;
        };


        const fillTexts = (
            ctx: CanvasRenderingContext2D,
            drawX: number,
            drawY: number,
            drawWidth: number,
            drawHeight: number
        ) => {
            const ratio = getPixelRatio();
            const content = props.content;
            const mergedFontSize = Number(fontSize.value) * ratio;

            ctx.font = `${fontStyle.value} normal ${fontWeight.value} ${mergedFontSize}px/${drawHeight}px ${fontFamily.value}`;
            ctx.fillStyle = color.value;
            ctx.textAlign = 'center';
            ctx.textBaseline = 'top';
            ctx.translate(drawWidth / 2, 0);

            const contents = Array.isArray(content) ? content : [content];
            contents?.forEach((item: string, index) => {
                ctx.fillText(item ?? '', drawX, drawY + index * (mergedFontSize + FontGap * ratio));
            });
        };

        const renderWatermark = () => {
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            const image = props.image;
            const rotate = props.rotate ?? -22;

            if (ctx) {
                if (!watermarkRef.value) {
                    watermarkRef.value = document.createElement('div');
                }

                const ratio = getPixelRatio();
                const [markWidth, markHeight] = getMarkSize(ctx);
                const canvasWidth = (gapX.value + markWidth) * ratio;
                const canvasHeight = (gapY.value + markHeight) * ratio;

                canvas.setAttribute('width', `${canvasWidth * BaseSize}px`);
                canvas.setAttribute('height', `${canvasHeight * BaseSize}px`);

                const drawX = (gapX.value * ratio) / 2;
                const drawY = (gapY.value * ratio) / 2;
                const drawWidth = markWidth * ratio;
                const drawHeight = markHeight * ratio;
                const rotateX = (drawWidth + gapX.value * ratio) / 2;
                const rotateY = (drawHeight + gapY.value * ratio) / 2;
                const alternateDrawX = drawX + canvasWidth;
                const alternateDrawY = drawY + canvasHeight;
                const alternateRotateX = rotateX + canvasWidth;
                const alternateRotateY = rotateY + canvasHeight;

                ctx.save();
                rotateWatermark(ctx, rotateX, rotateY, rotate);

                if (image) {
                    const img = new Image();
                    img.onload = () => {
                        ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
                        ctx.restore();
                        rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
                        ctx.drawImage(img, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
                        appendWatermark(canvas.toDataURL(), markWidth);
                    };
                    img.crossOrigin = 'anonymous';
                    img.referrerPolicy = 'no-referrer';
                    img.src = image;
                } else {
                    fillTexts(ctx, drawX, drawY, drawWidth, drawHeight);
                    ctx.restore();
                    rotateWatermark(ctx, alternateRotateX, alternateRotateY, rotate);
                    fillTexts(ctx, alternateDrawX, alternateDrawY, drawWidth, drawHeight);
                    appendWatermark(canvas.toDataURL(), markWidth);
                }
            }
        };

        const onMutate = (mutations: MutationRecord[]) => {
            if (stopObservation.value) return;

            mutations.forEach(mutation => {
                if (watermarkRef.value && reRendering(mutation, watermarkRef.value)) {
                    destroyWatermark();
                    renderWatermark();
                }
            });
        };

        watchEffect(() => {
            renderWatermark();
        });

        // 生命周期
        onMounted(() => {
            observer.value = new MutationObserver(onMutate);
            if (containerRef.value) {
                observer.value.observe(containerRef.value, {
                    attributes: true,
                    subtree: true,
                    childList: true,
                    attributeFilter: ['style', 'class']
                });
            }
        });

        onUnmounted(() => {
            destroyWatermark();
            observer.value?.disconnect();
        });

        return () => (
            <div ref={containerRef} class="cm-watermark" style={{position: 'relative'}}>
                {slots.default?.()}
            </div>
        );
    }
});
